﻿using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;
using UnityEngine.Video;
using Object = UnityEngine.Object;

namespace winS.UnityEditor
{
    /// <summary>
    /// Provides asset management for Editor.
    /// </summary>
    public static class EditorAssetManager
    {
        private static readonly string[] cache = new string[1];

        /// <summary>
        /// 将相对于Assets文件夹的路径改为全路径
        /// </summary>
        public static string AssetsPathToFullPath(string pathRelativeAsset)
        {
            return $"{Application.dataPath}/{pathRelativeAsset}";
        }
        /// <summary>
        /// 将相对于Project文件夹的路径改为全路径
        /// </summary>
        public static string ProjectPathToFullPath(string pathRelativeProject)
        {
            string assetsDirectoryPath = Application.dataPath;
            return assetsDirectoryPath.Remove(assetsDirectoryPath.Length - 6, 6) + pathRelativeProject;
        }
        /// <summary>
        /// 将全路径改为相对于项目的路径
        /// </summary>
        public static string FullPathToProjectPath(string fullPath)
        {
            return FileUtil.GetProjectRelativePath(fullPath.Replace('\\', '/'));
        }

        /// <summary>
        /// 创建目录 <paramref name="directoryRelativeProject"/>
        /// </summary>
        public static void CreateDirectory(string directoryRelativeProject)
        {
            Directory.CreateDirectory(ProjectPathToFullPath(directoryRelativeProject));
            Refresh();
        }
        /// <summary>
        /// 创建一个 <typeparamref name="T"/> 类型的资源.
        /// 默认后缀名为 asset
        /// </summary>
        public static T CreateAsset<T>(string directoryRelativeProject, string name, bool refresh = true) where T : ScriptableObject
        {
            T asset = ScriptableObject.CreateInstance<T>();
            CreateAsset(asset, directoryRelativeProject, name, refresh);
            return asset;
        }
        /// <summary>
        /// 创建一个泛型资源.
        /// </summary>
        public static T CreateAsset<T>(string pathRelativeProject, bool refresh = true) where T : ScriptableObject
        {
            T asset = ScriptableObject.CreateInstance<T>();
            CreateAsset(asset, pathRelativeProject, refresh);
            return asset;
        }
        /// <summary>
        /// 创建一个资源.
        /// 默认后缀名为 asset
        /// </summary>
        public static void CreateAsset(Object instance, string directoryRelativeProject, string name, bool refresh = true)
        {
            CreateAsset(instance, $"{directoryRelativeProject}/{name}.asset", refresh);
        }
        /// <summary>
        /// 创建一个资源
        /// </summary>
        public static void CreateAsset(Object instance, string pathRelativeProject, bool refresh = true)
        {
            AssetDatabase.CreateAsset(instance, pathRelativeProject);
            if (refresh) Refresh();
        }

        /// <summary>
        /// 加载资源
        /// </summary>
        public static T LoadAsset<T>(string pathRelativeProject) where T : Object
        {
            return AssetDatabase.LoadAssetAtPath<T>(pathRelativeProject);
        }
        /// <summary>
        /// 加载资源通过GUID
        /// </summary>
        public static T LoadAssetByGUID<T>(string guid) where T : Object
        {
            string path = GUIDToAssetPath(guid);
            if (path == string.Empty) return default;
            return AssetDatabase.LoadAssetAtPath<T>(path);
        }

        public static Texture LoadTexture(string pathRelativeProject)
        {
            return LoadAsset<Texture>(pathRelativeProject);
        }
        public static GameObject LoadPrefab(string pathRelativeProject)
        {
            return LoadAsset<GameObject>(pathRelativeProject);
        }
        public static AudioClip LoadAudioClip(string pathRelativeProject)
        {
            return LoadAsset<AudioClip>(pathRelativeProject);
        }
        public static VideoClip LoadVideoClip(string pathRelativeProject)
        {
            return LoadAsset<VideoClip>(pathRelativeProject);
        }
        public static TextAsset LoadTextAsset(string pathRelativeProject)
        {
            return LoadAsset<TextAsset>(pathRelativeProject);
        }
        /// <summary>
        /// 实例化预设
        /// </summary>
        /// <param name="connectToPrefab">是否将实例关联到预设资产</param>
        public static GameObject ClonePrefab(GameObject prefab, bool connectToPrefab = false)
        {
            return connectToPrefab ? PrefabUtility.InstantiatePrefab(prefab) as GameObject : Object.Instantiate(prefab);
        }
        /// <summary>
        /// 加载并实例化预设
        /// </summary>
        /// <param name="connectToPrefab">是否将实例关联到预设资产</param>
        public static GameObject LoadPrefabAndClone(string pathRelativeProject, bool connectToPrefab = false)
        {
            return ClonePrefab(LoadPrefab(pathRelativeProject), connectToPrefab);
        }

        /// <summary>
        /// 删除资产/文件夹
        /// </summary>
        public static bool DeleteAsset(string pathRelativeProject, bool refresh = true)
        {
            bool deleted = AssetDatabase.DeleteAsset(pathRelativeProject);
            if (refresh && deleted) Refresh();
            return deleted;
        }

        /// <summary>
        /// 给定的资源路径是否存在
        /// </summary>
        public static bool Exists(string pathRelativeProject)
        {
            return AssetDatabase.AssetPathToGUID(pathRelativeProject) != string.Empty;
        }
        /// <summary>
        /// 检查给定的目录路径是否存在
        /// </summary>
        public static bool ExistsDirectory(string pathRelativeProject)
        {
            return AssetDatabase.IsValidFolder(pathRelativeProject);
        }

        /// <summary>
        /// 获取资产 <paramref name="assetObject"/> 的路径
        /// </summary>
        /// <returns>该资产相对于项目文件夹的路径</returns>
        public static string GetAssetPath(Object assetObject)
        {
            return AssetDatabase.GetAssetPath(assetObject);
        }
        /// <summary>
        /// 获取资产 <paramref name="assetObject"/> 的GUID
        /// </summary>
        public static string GetAssetGUID(Object assetObject)
        {
            return AssetPathToGUID(GetAssetPath(assetObject));
        }
        /// <summary>
        /// 获取资产 <paramref name="assetObject"/> 所在目录
        /// </summary>
        /// <returns>该资产相对于项目文件夹的目录</returns>
        public static string GetAssetDirectory(Object assetObject)
        {
            return Path.GetDirectoryName(GetAssetPath(assetObject));
        }

        /// <summary>
        /// 刷新资源
        /// </summary>
        public static void Refresh()
        {
            AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport);
        }
        /// <summary>
        /// 刷新资源(异步)
        /// </summary>
        public static void RefreshAsync()
        {
            AssetDatabase.Refresh();
        }

        /// <summary>
        /// 高亮路径为 <paramref name="pathRelativeProject"/> 的资源
        /// </summary>
        public static void PingAsset(string pathRelativeProject)
        {
            EditorGUIUtility.PingObject(AssetDatabase.LoadAssetAtPath<Object>(pathRelativeProject));
        }

        /// <summary>
        /// 获取资源路径通过GUID
        /// </summary>
        public static string GUIDToAssetPath(string guid)
        {
            return AssetDatabase.GUIDToAssetPath(guid);
        }
        /// <summary>
        /// 获取GUID通过资产路径
        /// </summary>
        public static string AssetPathToGUID(string pathRelativeProject)
        {
            return AssetDatabase.AssetPathToGUID(pathRelativeProject);
        }

        /// <summary>
        /// 加载指定路径下所有类型为 <typeparamref name="T"/> 的资产
        /// </summary>
        public static IEnumerable<T> LoadAllAssetsAtPath<T>(string pathRelativeProject) where T : Object
        {
            return GetFiles(pathRelativeProject, "asset").Select(path => AssetDatabase.LoadAssetAtPath<T>(path));
        }
        /// <summary>
        /// 加载指定路径下所有的 <see cref="TextAsset"/>
        /// </summary>
        public static IEnumerable<TextAsset> LoadAllTextAssetAtPath(string pathRelativeProject)
        {
            return LoadInternal<TextAsset>(pathRelativeProject, "t:TextAsset");
        }
        /// <summary>
        /// 加载指定路径下所有的 Prefab
        /// </summary>
        public static IEnumerable<GameObject> LoadAllGameObjectAtPath(string pathRelativeProject)
        {
            return LoadInternal<GameObject>(pathRelativeProject, "t:Prefab");
        }

        /// <summary>
        /// 获取指定路径下所有文件路径
        /// </summary>
        /// <returns>所有基于项目文件夹的文件路径</returns>
        public static IEnumerable<string> GetFiles(string pathRelativeProject, bool includeSubdirectory = true)
        {
            return Directory.EnumerateFiles(ProjectPathToFullPath(pathRelativeProject), "*", includeSubdirectory ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly).Select(path => FullPathToProjectPath(path.Replace('\\', '/')));
        }
        /// <summary>
        /// 获取指定路径下所有带有指定后缀名的文件路径
        /// </summary>
        /// <returns>所有基于项目文件夹的文件路径</returns>
        public static IEnumerable<string> GetFiles(string pathRelativeProject, string extension, bool includeSubdirectory = true)
        {
            return Directory.EnumerateFiles(ProjectPathToFullPath(pathRelativeProject), $"*.{extension}", includeSubdirectory ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly).Select(path => FullPathToProjectPath(path.Replace('\\', '/')));
        }

        /// <summary>
        /// 获取路径 <paramref name="pathRelativeProject"/> 下所有 <typeparamref name="T"/> 类型资源的路径
        /// </summary>
        public static IEnumerable<string> GetAssetPathsAtPath<T>(string pathRelativeProject)
        {
            return GetPathInternal(pathRelativeProject, $"t:{typeof(T).FullName}");
        }

        /// <summary>
        /// 获取路径 <paramref name="pathRelativeProject"/> 下所有 <see cref="TextAsset"/> 资源的路径
        /// </summary>
        public static IEnumerable<string> GetTextAssetPathsAtPath(string pathRelativeProject)
        {
            return GetPathInternal(pathRelativeProject, "t:TextAsset");
        }
        /// <summary>
        /// 获取路径 <paramref name="pathRelativeProject"/> 下所有 <see cref="Texture"/> 资源的路径
        /// </summary>
        public static IEnumerable<string> GetTexturePathsAtPath(string pathRelativeProject)
        {
            return GetPathInternal(pathRelativeProject, "t:Texture");
        }
        /// <summary>
        /// 获取路径 <paramref name="pathRelativeProject"/> 下的所有 预设 资源的路径
        /// </summary>
        public static IEnumerable<string> GetPrefabPathsAtPath(string pathRelativeProject)
        {
            return GetPathInternal(pathRelativeProject, "t:Prefab");
        }
        /// <summary>
        /// 获取路径 <paramref name="pathRelativeProject"/> 下的所有 场景 资源的路径
        /// </summary>
        public static IEnumerable<string> GetScenePathsAtPath(string pathRelativeProject)
        {
            return GetPathInternal(pathRelativeProject, "t:Scene");
        }
        /// <summary>
        /// 获取路径 <paramref name="pathRelativeProject"/> 下的所有 <see cref="AudioClip"/> 资源的路径
        /// </summary>
        public static IEnumerable<string> GetAudioClipPathsAtPath(string pathRelativeProject)
        {
            return GetPathInternal(pathRelativeProject, "t:AudioClip");
        }
        /// <summary>
        /// 获取路径 <paramref name="pathRelativeProject"/> 下的所有 <see cref="VideoClip"/> 资源的路径
        /// </summary>
        public static IEnumerable<string> GetVideoClipPathsAtPath(string pathRelativeProject)
        {
            return GetPathInternal(pathRelativeProject, "t:VideoClip");
        }

        private static IEnumerable<T> LoadInternal<T>(string pathRelativeProject, string filter) where T : Object
        {
            cache[0] = pathRelativeProject;
            foreach (var guid in AssetDatabase.FindAssets(filter, cache)) yield return AssetDatabase.LoadAssetAtPath<T>(AssetDatabase.GUIDToAssetPath(guid));
        }
        private static IEnumerable<string> GetPathInternal(string pathRelativeProject, string filter)
        {
            cache[0] = pathRelativeProject;
            foreach (var guid in AssetDatabase.FindAssets(filter, cache)) yield return AssetDatabase.GUIDToAssetPath(guid);
        }
    }
}